package com.web.document.service.impl;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.web.common.basic.api.ElasticsearchApi;
import com.web.common.basic.elasticsearch.entity.ElasticParam;
import com.web.common.basic.entity.ElasticsearchIndex;
import com.web.common.datasource.DataSourcesSymbol;
import com.web.common.document.entity.DocumentChapter;
import com.web.common.document.entity.DocumentContent;
import com.web.common.document.entity.DocumentInfo;
import com.web.common.document.entity.DocumentInfoShareUser;
import com.web.common.document.entity.ShareDocChapter;
import com.web.common.document.entity.ShareDocInfo;
import com.web.common.result.R;
import com.web.common.result.ResultEnum;
import com.web.common.security.FeignInnerEntity;
import com.web.common.user.entity.WebUser;
import com.web.document.mapper.DocumentChapterMapper;
import com.web.document.mapper.DocumentInfoMapper;
import com.web.document.mapper.DocumentInfoShareUserMapper;
import com.web.service.handler.auth.UserUtil;
import com.web.service.handler.error.CustomException;

@Service
public class ShareDocInfoServiceImpl {
	
	private static final ObjectMapper MAPPER = new ObjectMapper();
	
	static {
		SimpleModule module = new SimpleModule();
		module.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ISO_DATE_TIME));
		MAPPER.registerModule(module);
	}
	
	@Autowired
	private DocumentInfoMapper documentInfoMapper;
	
	@Autowired
	private DocumentInfoShareUserMapper documentInfoShareUserMapper;
	
	@Autowired
	private DocumentChapterMapper documentChapterMapper;
	
	@Autowired
	private DocumentInfoServiceImpl documentInfoServiceImpl;
	
	@Autowired
	private ElasticsearchApi elasticsearchApi;
	

	/**
	 * 获取分享文档信息。
	 * 只针对所有用户公开的文档信息。
	 * @param shareCode 分享码
	 * @return R<ShareDocInfo> 分享文档信息
	 *
	 * @author zhouhui
	 * @since 2023.12.18
	 */
	public R<ShareDocInfo> shareDocInfo(String shareCode){
		DocumentInfo doc = getDocInfo(shareCode);
		
		ShareDocInfo docInfo = new ShareDocInfo();
		docInfo.setShareCode(shareCode);
		docInfo.setTitle(doc.getTitle());
		
		List<ShareDocChapter> chapterList = documentChapterMapper.getChapterList(doc.getMainId());
		List<ShareDocChapter> treeList = documentInfoServiceImpl.buildTree(chapterList);
		docInfo.setChapterList(treeList);
		
		return R.ok(docInfo);
	}
	
	/**
	 * 获取文档信息。
	 * 根据文档的类型判断是否允许查看，0私有；1、公开；2指定人员
	 * @param shareCode 分享码
	 * @return DocumentInfo 文档信息
	 *
	 * @author zhouhui
	 * @since 2023.12.18
	 */
	private DocumentInfo getDocInfo(String shareCode) {
		LambdaQueryWrapper<DocumentInfo> docQuery = new LambdaQueryWrapper<>();
		docQuery.eq(DocumentInfo::getShareCode, shareCode);
		docQuery.eq(DocumentInfo::getDelFlag, DataSourcesSymbol.DEL_FLAG_VALUE_0);
		docQuery.in(DocumentInfo::getShareActivity, 1, 2);
		DocumentInfo doc = documentInfoMapper.selectOne(docQuery);
		if(doc == null) {
			throw new CustomException("未查询到文档信息");
		}
		if(doc.getShareActivity().intValue() == 2 && (doc.getExpirationDate() == null || LocalDateTime.now().isAfter(doc.getExpirationDate()))) {
			throw new CustomException("已超过分享时间");
		}
		if(doc.getShareType() == 0 || doc.getShareType() == 2) {
			//私有和指定人员
			isAllowQuery(doc);
		}
		return doc;
	}
	
	/**
	 * 是否允许查询文档信息
	 * @param doc 查询到的文档信息
	 *
	 * @author zhouhui
	 * @since 1.0.0
	 */
	private void isAllowQuery(DocumentInfo doc) {
		WebUser user = UserUtil.getUser();
		if(user == null) {
			throw new CustomException("未登录系统无法查询文档信息");
		}
		if(doc.getShareType() == 0 && !doc.getOwnerId().equals(user.getUserId())) {
			//私有
			throw new CustomException("暂无权限查看");
		}
		if(doc.getShareType() == 2) {
			LambdaQueryWrapper<DocumentInfoShareUser> query = new LambdaQueryWrapper<>();
			query.eq(DocumentInfoShareUser::getInfoId, doc.getMainId());
			query.eq(DocumentInfoShareUser::getUserId, user.getUserId());
			List<DocumentInfoShareUser> dataList = documentInfoShareUserMapper.selectList(query);
			if(dataList.isEmpty()) {
				throw new CustomException("暂无权限查看");
			}
		}
	}
	
	
	
	/**
	 * 获取分享文档的章节内容
	 * @param shareCode 分享码
	 * @param index 章节的索引
	 * @return R<DocumentContent> 文档章节内容
	 *
	 * @author zhouhui
	 * @since 2023.12.19 
	 */
	public R<DocumentContent> shareDocContent(String shareCode, String index){
		DocumentInfo doc = getDocInfo(shareCode);
		
		LambdaQueryWrapper<DocumentChapter> chapterQuery = new LambdaQueryWrapper<>();
		chapterQuery.eq(DocumentChapter::getContentIndex, index);
		chapterQuery.eq(DocumentChapter::getInfoId, doc.getMainId());
		chapterQuery.eq(DocumentChapter::getDelFlag, DataSourcesSymbol.DEL_FLAG_VALUE_0);
		DocumentChapter chapter = documentChapterMapper.selectOne(chapterQuery);
		if(chapter == null) {
			throw new CustomException("未查询到章节信息");
		}
		if(chapter.getType().equals(0)) {
			throw new CustomException("章节类型为目录，无文档内容");
		}else {
			if(!StringUtils.hasText(chapter.getContentIndex())) {
				throw new CustomException("未查询到文档索引");
			}
		}
		return R.ok(documentInfoServiceImpl.getContentByIndex(chapter.getContentIndex()));
	}
	
	/**
	 * 按照条件模糊查询文档内容
	 * @param shareCode 分享码
	 * @param content 查询的内容
	 * @return List<DocumentContent> 查询到的文档内容
	 *
	 * @author zhouhui
	 * @since 2024.02.13
	 */
	@SuppressWarnings("unchecked")
	public R<List<DocumentContent>> searchDocContent(String shareCode, String content) {
		DocumentInfo doc = getDocInfo(shareCode);
		
		List<String> contentIndexList = documentChapterMapper.getContentIndexList(doc.getMainId());
		if(contentIndexList == null || contentIndexList.isEmpty()) {
			return R.ok(new ArrayList<>());
		}
		
		ElasticParam param = new ElasticParam();
		param.setMethod(HttpMethod.GET.name());
		param.setEndpoint("/" + ElasticsearchIndex.DOCUMENT_INDEX + "/_search");
		param.setJsonEntity(setQueryParamJson(contentIndexList, content));
		R<Object> result = elasticsearchApi.customOperate(param, FeignInnerEntity.HTTP_INNER_VALUE);
		if(!result.getCode().equals(ResultEnum.SUCCESS.getCode())) {
			return R.fail(null, result.getMessage());
		}
		String dataStr = result.getData().toString();
		
		List<DocumentContent> dataList = new ArrayList<>();
		try {
			Map<String, Object> data = MAPPER.readValue(dataStr, Map.class);
			Map<String, Object> hitsMap = (Map<String, Object>) data.get("hits");
			List<Map<String, Object>> hitsList = (List<Map<String, Object>>) hitsMap.get("hits");
			for(Map<String, Object> item: hitsList) {
				Map<String, Object> source = (Map<String, Object>) item.get("_source");
				
				DocumentContent contentItem = new DocumentContent();
				contentItem.setInfoId(Integer.valueOf(source.get("infoId").toString()));
				contentItem.setDocumentId(source.get("documentId").toString());
				
				Map<String, Object> highlight = (Map<String, Object>) item.get("highlight");
				List<String> highlightList = (List<String>) highlight.get("contentText");
				if(highlightList != null && !highlightList.isEmpty()) {
					contentItem.setContentText(highlightList.get(0));
				}
				dataList.add(contentItem);
			}
		} catch (JsonProcessingException e) {
			e.printStackTrace();
		}
		return R.ok(dataList);
	}
	
	/**
	 * 设置查询字符串
	 * @param contentIndexList 内容索引列表 
	 * @param searchContent 查询条件
	 * @return String 参数字符串
	 *
	 * @author zhouhui
	 * @since 2024.02.13
	 */
	private String setQueryParamJson(List<String> contentIndexList, String searchContent) {
		StringBuilder result = new StringBuilder();
		StringBuilder listStr = new StringBuilder();
		for (int i = 0; i < contentIndexList.size(); i++) {
			if(i > 0) {
				listStr.append(",");
			}
			listStr.append("\"");
			listStr.append(contentIndexList.get(i));
			listStr.append("\"");
		}
		String str = "{\"query\":{\"bool\":{\"filter\":[{\"terms\":{\"documentId\":"
				+ "["+ listStr.toString() +"]}}],"
				+ "\"must\":[{\"match\":{\"contentText\":\""+ searchContent +"\"}}]}},\"_source\":[\"infoId\",\"documentId\"],"
				+ "\"highlight\":{\"fields\":{\"contentText\":{}},\"fragment_size\":20}}";
		result.append(str);
		return result.toString();
	}
 }
